/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ts, NodePredicate, rule } from 'qvog-dsl';

const SEVERITY = 2;
const DOC_PATH = 'docs/max-depth.md';
const DEFAULT_MAX_DEPTH = 4;

// 定义需要检查的节点类型
const BlockLikeNode: NodePredicate<ts.Node> = {
  is(node: ts.Node): node is ts.Node {
    return ts.isBlock(node) ||
      ts.isIfStatement(node) ||
      ts.isForStatement(node) ||
      ts.isWhileStatement(node) ||
      ts.isDoStatement(node) ||
      ts.isForInStatement(node) ||
      ts.isForOfStatement(node) ||
      ts.isSwitchStatement(node) ||
      ts.isTryStatement(node) ||
      ts.isWithStatement(node);
  }
};

interface RuleOption {
  max?: number;
}

export default rule<{ ruleOption: RuleOption }>()
  .match(BlockLikeNode)
  .when((node: ts.Node, { props }): boolean => {
    // 获取配置
    const options = props.ruleOption;
    const maxDepth = options.max ?? DEFAULT_MAX_DEPTH;

    // 跳过函数边界和else if
    if (isFunctionBoundary(node) || isElseIfClause(node)) {
      return false;
    }

    // 计算当前嵌套深度
    const depth = calculateNestingDepth(node);
    return depth > maxDepth;
  })
  .report({
    severity: SEVERITY,
    description: `Maximum allowed nesting depth is ${DEFAULT_MAX_DEPTH}`,
    docPath: DOC_PATH
  });

// 辅助函数
function isFunctionBoundary(node: ts.Node): boolean {
  return ts.isFunctionLike(node) ||
    ts.isSourceFile(node) ||
    ts.isClassStaticBlockDeclaration(node);
}

function isElseIfClause(node: ts.Node): boolean {
  return ts.isIfStatement(node) &&
    ts.isIfStatement(node.parent) &&
    node.parent.elseStatement === node;
}

function calculateNestingDepth(node: ts.Node): number {
  let depth = 0;
  let current = node.parent;

  while (current) {
    if (BlockLikeNode.is(current) && !isFunctionBoundary(current)) {
      depth++;
    }
    current = current.parent;
  }

  return depth;
}
